void WIN32_OP_FAIL();
#ifdef _DEBUG
void WIN32_OP_D_FAIL(const wchar_t * _Message, const wchar_t *_File, unsigned _Line);
#endif

//Throws an exception when (OP) evaluates to false/zero.
#define WIN32_OP(OP)	\
	{	\
		SetLastError(NO_ERROR);	\
		if (!(OP)) WIN32_OP_FAIL();	\
	}

//WIN32_OP_D() acts like an assert specialized for win32 operations in debug build, ignores the return value / error codes in release build.
//Use WIN32_OP_D() instead of WIN32_OP() on operations that are extremely unlikely to fail, so failure condition checks are performed in the debug build only, to avoid bloating release code with pointless error checks.
#ifdef _DEBUG
#define WIN32_OP_D(OP)	\
	{	\
		SetLastError(NO_ERROR); \
		if (!(OP)) WIN32_OP_D_FAIL(PFC_WIDESTRING(#OP), PFC_WIDESTRING(__FILE__), __LINE__); \
	}

#else
#define WIN32_OP_D(OP) (void)( (OP), 0);
#endif


class registerclass_scope_delayed
{
public:
    registerclass_scope_delayed() : m_class(0) {}

    bool is_registered() const
    {
        return m_class != 0;
    }
    void toggle_on(UINT p_style,WNDPROC p_wndproc,int p_clsextra,int p_wndextra,HICON p_icon,HCURSOR p_cursor,HBRUSH p_background,const TCHAR * p_classname,const TCHAR * p_menuname);
    void toggle_off();
    ATOM get_class() const
    {
        return m_class;
    }

    ~registerclass_scope_delayed()
    {
        toggle_off();
    }
private:
    registerclass_scope_delayed(const registerclass_scope_delayed &)
    {
        throw pfc::exception_not_implemented();
    }
    const registerclass_scope_delayed & operator=(const registerclass_scope_delayed &)
    {
        throw pfc::exception_not_implemented();
    }

    ATOM m_class;
};



template<typename t_object>
class syncd_storage
{
private:
    typedef syncd_storage<t_object> t_self;
public:
    syncd_storage() {}
    template<typename t_source>
    syncd_storage(const t_source & p_source) : m_object(p_source) {}
    template<typename t_source>
    void set(t_source const & p_in)
    {
        insync(m_sync);
        m_object = p_in;
    }
    template<typename t_destination>
    void get(t_destination & p_out) const
    {
        insync(m_sync);
        p_out = m_object;
    }
    t_object get() const
    {
        insync(m_sync);
        return m_object;
    }
    template<typename t_source>
    const t_self & operator=(t_source const & p_source)
    {
        set(p_source);
        return *this;
    }
private:
    mutable critical_section m_sync;
    t_object m_object;
};

template<typename t_object>
class syncd_storage_flagged
{
private:
    typedef syncd_storage_flagged<t_object> t_self;
public:
    syncd_storage_flagged() : m_changed_flag(false) {}
    template<typename t_source>
    syncd_storage_flagged(const t_source & p_source) : m_changed_flag(false), m_object(p_source) {}
    void set_changed(bool p_flag = true)
    {
        insync(m_sync);
        m_changed_flag = p_flag;
    }
    template<typename t_source>
    void set(t_source const & p_in)
    {
        insync(m_sync);
        m_object = p_in;
        m_changed_flag = true;
    }
    bool has_changed() const
    {
        insync(m_sync);
        return m_changed_flag;
    }
    t_object peek() const
    {
        insync(m_sync);
        return m_object;
    }
    template<typename t_destination>
    bool get_if_changed(t_destination & p_out)
    {
        insync(m_sync);
        if (m_changed_flag)
        {
            p_out = m_object;
            m_changed_flag = false;
            return true;
        }
        else
        {
            return false;
        }
    }
    t_object get()
    {
        insync(m_sync);
        m_changed_flag = false;
        return m_object;
    }
    template<typename t_destination>
    void get(t_destination & p_out)
    {
        insync(m_sync);
        p_out = m_object;
        m_changed_flag = false;
    }
    template<typename t_source>
    const t_self & operator=(t_source const & p_source)
    {
        set(p_source);
        return *this;
    }
private:
    bool m_changed_flag;
    mutable critical_section m_sync;
    t_object m_object;
};

typedef CGlobalLockScope CGlobalLock;//for compatibility, implementation moved elsewhere

static bool SetClipboardDataBlock(UINT p_format,const void * p_block,t_size p_block_size)
{
    bool success = false;
    if (OpenClipboard(NULL))
    {
        EmptyClipboard();
        HANDLE handle = GlobalAlloc(GMEM_MOVEABLE,p_block_size);
        if (handle == NULL)
        {
            CloseClipboard();
            throw std::bad_alloc();
        }
        {
            CGlobalLock lock(handle);
            memcpy(lock.GetPtr(),p_block,p_block_size);
        }
        if (SetClipboardData(p_format,handle) == NULL)
        {
            GlobalFree(handle);//todo?
        }
        else
        {
            success = true;
        }
        CloseClipboard();
    }
    return success;
}

template<typename t_array>
static bool SetClipboardDataBlock(UINT p_format,const t_array & p_array)
{
    pfc::static_assert<sizeof(p_array[0]) == 1>();
    return SetClipboardDataBlock(p_format,p_array.get_ptr(),p_array.get_size());
}

template<typename t_array>
static bool GetClipboardDataBlock(UINT p_format,t_array & p_array)
{
    pfc::static_assert<sizeof(p_array[0]) == 1>();
    if (OpenClipboard(NULL))
    {
        HANDLE handle = GetClipboardData(p_format);
        if (handle == NULL)
        {
            CloseClipboard();
            return false;
        }
        {
            CGlobalLock lock(handle);
            const t_size size = lock.GetSize();
            try
            {
                p_array.set_size(size);
            }
            catch (...)
            {
                CloseClipboard();
                throw;
            }
            memcpy(p_array.get_ptr(),lock.GetPtr(),size);
        }
        CloseClipboard();
        return true;
    }
    else
    {
        return false;
    }
}


class OleInitializeScope
{
public:
    OleInitializeScope()
    {
        if (FAILED(OleInitialize(NULL))) throw pfc::exception("OleInitialize() failure");
    }
    ~OleInitializeScope()
    {
        OleUninitialize();
    }

private:
    PFC_CLASS_NOT_COPYABLE(OleInitializeScope,OleInitializeScope);
};

class CoInitializeScope
{
public:
    CoInitializeScope()
    {
        if (FAILED(CoInitialize(NULL))) throw pfc::exception("CoInitialize() failed");
    }
    ~CoInitializeScope()
    {
        CoUninitialize();
    }
    PFC_CLASS_NOT_COPYABLE_EX(CoInitializeScope)
};


unsigned QueryScreenDPI();

SIZE QueryScreenDPIEx();

static WORD GetOSVersion()
{
    const DWORD ver = GetVersion();
    return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8);
}

#if _WIN32_WINNT >= 0x501
#define WS_EX_COMPOSITED_Safe() WS_EX_COMPOSITED
#else
static DWORD WS_EX_COMPOSITED_Safe()
{
    return (GetOSVersion() < 0x501) ? 0 : 0x02000000L;
}
#endif


static t_size GetOptimalWorkerThreadCount() throw()
{
    DWORD_PTR mask,system;
    t_size ret = 0;
    GetProcessAffinityMask(GetCurrentProcess(),&mask,&system);
    for (t_size n=0;n<sizeof(mask)*8;n++)
    {
        if (mask & ((DWORD_PTR)1<<n)) ret++;
    }
    if (ret == 0) return 1;
    return ret;
}

//! IMPORTANT: all classes derived from CVerySimpleThread must call WaitTillThreadDone() in their destructor, to avoid object destruction during a virtual function call!
class CVerySimpleThread
{
public:
    CVerySimpleThread() : m_thread(INVALID_HANDLE_VALUE) {}
    ~CVerySimpleThread()
    {
        WaitTillThreadDone();
    }
    void StartThread()
    {
        CloseThread();
        HANDLE thread;
        WIN32_OP( (thread = CreateThread(NULL,0,g_entry,reinterpret_cast<void*>(this),0,NULL) ) != NULL);
        m_thread = thread;
    }
    bool IsThreadActive() const
    {
        return m_thread != INVALID_HANDLE_VALUE;
    }
    void WaitTillThreadDone()
    {
        CloseThread();
    }
protected:
    virtual void ThreadProc() {}
private:
    void CloseThread()
    {
        if (IsThreadActive())
        {
            WaitForSingleObject(m_thread,INFINITE);
            CloseHandle(m_thread);
            m_thread = INVALID_HANDLE_VALUE;
        }
    }

    static DWORD CALLBACK g_entry(void* p_instance)
    {
        return reinterpret_cast<CVerySimpleThread*>(p_instance)->entry();
    }
    unsigned entry()
    {
        try
        {
            ThreadProc();
        }
        catch (...) {}
        return 0;
    }
    HANDLE m_thread;

    PFC_CLASS_NOT_COPYABLE_EX(CVerySimpleThread)
};

//! IMPORTANT: all classes derived from CSimpleThread must call AbortThread()/WaitTillThreadDone() in their destructors, to avoid object destruction during a virtual function call!
class CSimpleThread : private completion_notify_receiver
{
public:
    CSimpleThread() : m_thread(INVALID_HANDLE_VALUE) {}
    ~CSimpleThread()
    {
        AbortThread();
    }
    void StartThread()
    {
        AbortThread();
        m_abort.reset();
        m_ownNotify = create_task(0);
        HANDLE thread;
        WIN32_OP( (thread = CreateThread(NULL,0,g_entry,reinterpret_cast<void*>(this),0,NULL) ) != NULL);
        m_thread = thread;
    }
    void AbortThread()
    {
        m_abort.abort();
        CloseThread();
    }
    bool IsThreadActive() const
    {
        return m_thread != INVALID_HANDLE_VALUE;
    }
    void WaitTillThreadDone()
    {
        CloseThread();
    }
protected:
    virtual unsigned ThreadProc(abort_callback & p_abort)
    {
        return 0;
    }
    //! Called when the thread has completed normally, with p_code equal to ThreadProc retval. Not called when AbortThread() or WaitTillThreadDone() was used to abort the thread / wait for the thread to finish.
    virtual void ThreadDone(unsigned p_code) {};
private:
    void CloseThread()
    {
        if (IsThreadActive())
        {
            WaitForSingleObject(m_thread,INFINITE);
            CloseHandle(m_thread);
            m_thread = INVALID_HANDLE_VALUE;
        }
        orphan_all_tasks();
    }

    void on_task_completion(unsigned p_id,unsigned p_status)
    {
        if (IsThreadActive())
        {
            CloseThread();
            ThreadDone(p_status);
        }
    }
    static DWORD CALLBACK g_entry(void* p_instance)
    {
        return reinterpret_cast<CSimpleThread*>(p_instance)->entry();
    }
    unsigned entry()
    {
        unsigned code = ~0;
        try
        {
            code = ThreadProc(m_abort);
        }
        catch (...) {}
        if (!m_abort.is_aborting()) m_ownNotify->on_completion_async(code);
        return code;
    }
    abort_callback_impl m_abort;
    HANDLE m_thread;
    completion_notify_ptr m_ownNotify;

    PFC_CLASS_NOT_COPYABLE_EX(CSimpleThread);
};



class EnableWindowScope
{
public:
    EnableWindowScope(HWND p_window,BOOL p_state) throw() : m_window(p_window)
    {
        m_oldState = IsWindowEnabled(m_window);
        EnableWindow(m_window,p_state);
    }
    ~EnableWindowScope() throw()
    {
        EnableWindow(m_window,m_oldState);
    }

private:
    BOOL m_oldState;
    HWND m_window;
};

bool IsMenuNonEmpty(HMENU menu);

class SetTextColorScope
{
public:
    SetTextColorScope(HDC dc, COLORREF col) throw() : m_dc(dc)
    {
        m_oldCol = SetTextColor(dc,col);
    }
    ~SetTextColorScope() throw()
    {
        SetTextColor(m_dc,m_oldCol);
    }
    PFC_CLASS_NOT_COPYABLE_EX(SetTextColorScope)
private:
    HDC m_dc;
    COLORREF m_oldCol;
};

class CloseHandleScope
{
public:
    CloseHandleScope(HANDLE handle) throw() : m_handle(handle) {}
    ~CloseHandleScope() throw()
    {
        CloseHandle(m_handle);
    }
    HANDLE Detach() throw()
    {
        return pfc::replace_t(m_handle,INVALID_HANDLE_VALUE);
    }
    HANDLE Get() const throw()
    {
        return m_handle;
    }
    void Close() throw()
    {
        CloseHandle(Detach());
    }
    PFC_CLASS_NOT_COPYABLE_EX(CloseHandleScope)
private:
    HANDLE m_handle;
};

class CModelessDialogEntry
{
public:
    inline CModelessDialogEntry() : m_wnd() {}
    inline CModelessDialogEntry(HWND p_wnd) : m_wnd()
    {
        Set(p_wnd);
    }
    inline ~CModelessDialogEntry()
    {
        Set(NULL);
    }

    void Set(HWND p_new)
    {
        static_api_ptr_t<modeless_dialog_manager> api;
        if (m_wnd) api->remove(m_wnd);
        m_wnd = p_new;
        if (m_wnd) api->add(m_wnd);
    }
private:
    PFC_CLASS_NOT_COPYABLE_EX(CModelessDialogEntry);
    HWND m_wnd;
};
